package hl.alde;

public class Knapsack_01 {

    // 0/1背包算法，填表x,f，找最大价值
    public static void knapSack(int c, int n, int x[], int w[], int v[], int f[][]){
        // 填表f[][]，第0行，第0列为0 ，即f[i][0] = f[0][j] = 0
        for (int i=0; i<=n; i++)
            f[i][0] = 0;
        for (int j=0; j<=c; j++)
            f[0][j] = 0;
        // 填表f[][]，其他位置 f[i][j] = 核心公式
        // 表f 的意义是记录 背包容量从0到c 时的每个容量时的背包能有的最大价值
        for (int i=1; i<=n; i++) {    // 依次计算第i行，第0行已经填完，从 1 开始
            for (int j=1; j<=c; j++){ // 依次计算第i行每个元素，第0列已经填完，从 1 开始
                if (w[i] > j)// 如果 第i个物品的重量>背包当前容量 ，即第i个物品不可装
                    f[i][j] = f[i-1][j];    // 最大价值量不变
                else         // 如果 第i个物品可装入，比较一下是装入价值大，还是不装入价值大
                    f[i][j] = Math.max(f[i-1][j], v[i] + f[i-1][j-w[i]]);
/* 此处，我原来有个错误认识，可以装，可以不装，那肯定装了价值大啊，
   原来的理解错在哪，原来的理解是基于装进背包的物品就不在拿出来了，那肯定是越装越多，但是
   v[i] + f[i-1][j-w[i]] 的意思是 当前此物品的价值+(当前背包容量-此物品重量)时的最大物品价值(此种情况下可能会造成原来装入的物品已经被拿出来了)
*/
            }
        }
        // 打印 f 表的值
        System.out.println("表 f 的值：");
        for (int i=0; i<=n; i++){
            for (int j=0; j<=c; j++)
                System.out.printf("%-4d" , f[i][j]);
            System.out.println();
        }

        // 填表 x[i]，计算总价值
        int j = c;
        for (int i=n; i>0; i--) {
            if (f[i][j] > f[i-1][j]) {    // 如果 第i个物品装入
                x[i] = 1;    // x[i] 置1
                j -= w[i];   // 背包重量减去 w[i]
            }
            else    // 如果 第i个物品不装入
                x[i] = 0;    // x[i] 置0，背包重量不变
        }
        // 打印 x 表的值 和 总价值
        System.out.println("表 x 的值：");
        for (int i=1; i<=n; i++)
            System.out.print("x" + i + " = " + x[i] + "\t");
        System.out.println();
        System.out.printf("总价值为: %d", f[n][c]);
    }

    public static void main(String[] args) {
/*
        int c = 10;     // 背包总重量为 10
        int n = 5;      // 总的物品数为 5
        int x[] = new int[n+1]; // 是否装入 xi，即 xi = 1/0  (x[0]]位置不用)
        int w[] = new int[]{0,2,2,6,5,4};// xi 的重量 (w[0]位置不用)
        int v[] = new int[]{0,6,3,5,4,6};// xi 的价值 (v[0]位置不用)
        int f[][] = new int[n+1][c+1];   // f[i][j] 前i个物品装入容量为j的包 所得到的最大价值

        knapSack(c, n, x, w, v, f); // 调用0/1背包算法，填表x,f，找最大价值
*/
        int c = 5;     // 背包总重量为 10
        int n = 4;      // 总的物品数为 5
        int x[] = new int[n+1]; // 是否装入 xi，即 xi = 1/0  (x[0]]位置不用)
        int w[] = new int[]{0,2,1,3,2};// xi 的重量 (w[0]位置不用)
        int v[] = new int[]{0,12,10,20,15};// xi 的价值 (v[0]位置不用)
        int f[][] = new int[n+1][c+1];   // f[i][j] 前i个物品装入容量为j的包 所得到的最大价值

        knapSack(c, n, x, w, v, f); // 调用0/1背包算法，填表x,f，找最大价值
    }
}
